/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_kernel.htm
 *
 * Zhiqim Kernel is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.kernel.util;

import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.zhiqim.kernel.annotation.AnAlias;
import org.zhiqim.kernel.constants.CodeConstants;
import org.zhiqim.kernel.constants.EnumConstants;
import org.zhiqim.kernel.constants.RegexpConstants;
import org.zhiqim.kernel.constants.SignConstants;

/**
 * 验证相关工具类
 * 1.继承Validatex类，实现基于正则表达式的验证
 * 2.创建Validates类，实现扩展的方法验证
 * 
 * @version v1.0.0 @author zouzhigang 2014-2-27 新建与整理
 */
@AnAlias("Validates")
public class Validates extends Validatex implements SignConstants, EnumConstants, CodeConstants, RegexpConstants
{
    /*************************************/
    //字符验证
    /*************************************/

    /** 是否空白字符，小于等于32认为是空白字符，128-160认为是空白符，12288是中文大写空格,65279是BOM头空格 */
    public static boolean isWhitespace(char c)
    {
        return c <= _SPACE_ || (c >= _128_ && c <= _160_) || c == _SPACE_ZH_ || c == _SPACE_BOM_;
    }
    
    /** 如果s中存在c,则返回true,否则返回false */
    public static boolean isCharInString(char c, String s)
    {
        return (s.indexOf(c) != -1);
    }

    /** 检查字符是否是大写字母,(注:A-Z之间) */
    public static boolean isLetterUppercase(char c)
    {
        return _LETTERS_UPPERCASE_.indexOf(c) != -1;
    }
    
    /** 检查字符是否是大写字母加数字,(注:A-Z,0-9之间) */
    public static boolean isLetterUppercaseDigits(char c)
    {
        return _LETTERS_UPPERCASE_DIGITS_.indexOf(c) != -1;
    }
    
    /** 检查字符是否是小写字母,(注:a-z之间) */
    public static boolean isLetterLowercase(char c)
    {
        return _LETTERS_LOWERCASE_.indexOf(c) != -1;
    }
    
    /** 检查字符是否是小写字母加数字,(注:a-z,0-9之间) */
    public static boolean isLetterLowercaseDigits(char c)
    {
        return _LETTERS_LOWERCASE_DIGITS_.indexOf(c) != -1;
    }
    
    /** 检查字符是否是字母,(注:a-z,A-Z之间) */
    public static boolean isLetter(char c)
    {
        return _LETTERS_.indexOf(c) != -1;
    }

    /** 检查字符是否是数字 */
    public static boolean isDigit(char c)
    {
        return _DIGITS_.indexOf(c) != -1;
    }
    
    /** 检查字符是否是数字或小数点 */
    public static boolean isDigitOrDot(char c)
    {
        return isDigit(c) || c == '.';
    }

    /** 检查字符是否是数字或字母 */
    public static boolean isLetterOrDigit(char c)
    {
        return _LETTERS_DIGITS_.indexOf(c) != -1;
    }
    
    /*************************************/
    //基础判断
    /*************************************/
    
    /** 是否Windows */
    public static boolean isWindows()
    {
        return Systems.isWindows();
    }
    
    /** 是否MacOS */
    public static boolean isMacOS()
    {
        return Systems.isMacOS();
    }
    
    /** 是否Linux */
    public static boolean isLinux()
    {
        return Systems.isLinux();
    }
    
    /** 检查两个对象是否相等 */
    public static boolean isEqual(Object obj, Object obj2)
    {
        if (obj == null)
            return obj2 == null;
        
        if (obj2 == null)
            return false;
        
        if (obj.getClass() != obj2.getClass())
            return false;
        
        return obj.equals(obj2);
    }
    
    /** 检查两个对象是否相等 */
    public static boolean isNotEqual(Object obj, Object obj2)
    {
        return !isEqual(obj, obj2);
    }

    /** 检查两个字符串是否忽略大小写相等 */
    public static boolean isEqualIgnoreCase(String str, String str2)
    {
        return (str == null)?str2 == null:str.equalsIgnoreCase(str2);
    }
    
    /** 检查src是否包括str */
    public static boolean isIndexOf(String src, String str)
    {
        return (src == null)?(str == null):(src.indexOf(str) != -1);
    }
    
    /** 检查对象是否为空 */
    public static boolean isEmpty(Object obj)
    {
        if (obj == null)
            return true;
        
        if (obj instanceof String)
            return isEmpty((String)obj);
        
        if (obj instanceof Collection)
            return isEmpty((Collection<?>)obj);
        
        if (obj instanceof Map)
            return ((Map<?,?>)obj).isEmpty();
        
        if (Types.isArray(obj))
            return Arrays.isEmpty(obj);
        
        return false;
    }
    
    /** 检查字符串是否为空，null或空字符串 */
    public static boolean isEmpty(String s)
    {
        return s == null || s.length() == 0;
    }

    /** 检查字符串是否为空白字符串 */
    public static boolean isEmptyBlank(String s)
    {
        return s == null || Strings.trim(s).length() == 0;
    }
    
    /** 检查集合是否为null或空 */
    public static boolean isEmpty(Collection<?> c)
    {
        return c == null || c.size() == 0;
    }
    
    /** 检查数组是否为null或空 */
    public static boolean isEmpty(Object[] s)
    {
        return s == null || s.length == 0;
    }
    
    /** 检查字符串是否不为空，即不为null和空字符串 */
    public static boolean isNotEmpty(String s)
    {
        return !isEmpty(s);
    }

    /** 检查字符串是否不为空，即不为null和空白字符串 */
    public static boolean isNotEmptyBlank(String s)
    {
        return !isEmptyBlank(s);
    }
    
       /** 检查集合是否不为空 */
    public static boolean isNotEmpty(Collection<?> c)
    {
        return !isEmpty(c);
    }
    
    /** 检查是否不为空 */
    public static boolean isNotEmpty(Object obj)
    {
        return !isEmpty(obj);
    }

    /** 检查是否是指定的长度，注:当s=null || s="",min=0时为true */
    public static boolean isLen(String s, int min, int max)
    {
        if (isEmpty(s))
            return min == 0;
        
        return (s.length() >= min && s.length() <= max);
    }
    
    /** 检查是否GBK编码长度，字母占1个，汉字占2个 */
    public static boolean isByteLen(String s, int min, int max)
    {
        return isByteLen(s, min, max, _GBK_);
    }
    
    /** 检查是否指定编码长度，UTF-8是一个汉字3个字节，GBK是两个 */
    public static boolean isByteLen(String s, int min, int max, String encoding)
    {
        if (isEmpty(s))
            return min == 0;
        
        int len = Bytes.getByteLen(s, encoding);
        return (len >= min && len <= max);
    }
    
    /** 检查是否在指定的字符串内 */
    public static boolean isScope(String s, String scope)
    {
        if (isEmpty(s))
            return false;
            
        // 逐个字符检查,如果发现一个不是specifyStr,则返回false
        for (int i=0;i<s.length();i++)
        {
            char c = s.charAt(i);

            if (!isCharInString(c, scope))
                return false;
        }
        
        return true;
    }
    
    /** 检查是否是正确的电话号码 */
    public static boolean isPhone(String s)
    {
        return isScope(s, _PHONE_NUMBER_CHARS);
    }
    
    /** 检查是否是有效的颜色 */
    public static boolean isColor(String s)
    {
        if (!Validates.isLen(s, 7, 7) || !s.startsWith("#"))
            return false;
        
        s = s.substring(1);
        return isScope(s, _DIGITS_HEX_);
    }
    
    /** 是否包含多字节字符 */
    public static boolean isContainMultByteChar(String s)
    {
        if (isEmpty(s))
            return false;

        int len = s.length();
        for (int i=0;i<len;i++)
        {
            if ((int)s.charAt(i) > 255)
                return true;
        }

        return false;
    }
    
    /*************************************/
    //正则表达式相关
    /*************************************/
    
    /** 检查是否符合正则表达式 */
    public static boolean isRegexp(String regexp, String s)
    {
        return isRegexp(regexp, s, false);
    }
    
    /** 检查是否是布尔型，true|false */
    public static boolean isBoolean(String s)
    {
        return isBoolean(s, false);
    }
    
    /** 检查是否是数值型，允许0开头 */
    public static boolean isNumeric(String s)
    {
        return isNumeric(s, false);
    }

    /** 检查是否数值型，允许0开头且长度在指定范围内 */
    public static boolean isNumericLen(String s, int min, int max)
    {
        if (!isNumeric(s, min == 0))
            return false;
        
        return (s.length() >= min && s.length() <= max);
    }
    
    /** 检查是否是整型 */
    public static boolean isInteger(String s)
    {
        return isInteger(s, false);
    }

    /** 检查是否是一个正整数 */
    public static boolean isIntegerPositive(String s)
    {
        return isIntegerPositive(s, false);
    }

    /** 检查是否是一个非负整数 */
    public static boolean isIntegerNonnegative(String s)
    {
        return isIntegerNonnegative(s, false);
    }

    /** 检查是否是一个负整数 */
    public static boolean isIntegerNegative(String s)
    {
        return isIntegerNegative(s, false);
    }

    /** 检查是否是一个非正整数 */
    public static boolean isIntegerNonpositive(String s)
    {
        return isIntegerNonpositive(s, false);
    }
    
    /** 检查字符串是否是整型,且min <= s <= max */
    public static boolean isIntegerValue(String s, int min, int max)
    {
        if (!isInteger(s, false))
            return false;

        int num = Integer.parseInt(s);
        return ((num >= min) && (num <= max));
    }
    
    /** 检查字符串是否是非负整型,且min <= s.length() <= max */
    public static boolean isIntegerNonnegativeLen(String s, int min, int max)
    {
        if (Validates.isEmpty(s))
            return min == 0;
        
        if (!isIntegerNonnegative(s, false))
            return false;
        
        return ((s.length() >= min) && (s.length() <= max));
    }
    
    /** 检查字符串是否都是由字母组成 */
    public static boolean isAlphabetic(String s)
    {
        return isAlphabetic(s, false);
    }
    
    /** 检查字符串是否都是由小写字母组成 */
    public static boolean isAlphabeticLowercase(String s)
    {
        return isAlphabeticLowercase(s, false);
    }
    
    /** 检查字符串是否都是由大写字母组成 */
    public static boolean isAlphabeticUppercase(String s)
    {
        return isAlphabeticUppercase(s, false);
    }
    
    /** 检查字符串是否都是由字母组成且长度在min,max范围内 */
    public static boolean isAlphabeticLen(String s, int min, int max)
    {
        if (Validates.isEmpty(s))
            return min == 0;
        
        if (!isAlphabetic(s, false))
            return false;
        
        return (s.length() >= min && s.length() <= max);
    }
    
    /** 检查字符串是否都是由小写字母组成且长度在min,max范围内 */
    public static boolean isAlphabeticLowercaseLen(String s, int min, int max)
    {
        if (Validates.isEmpty(s))
            return min == 0;
        
        if (!isAlphabeticLowercase(s, false))
            return false;
        
        return (s.length() >= min && s.length() <= max);
    }
    
    /** 检查字符串是否都是由大字母组成且长度在min,max范围内 */
    public static boolean isAlphabeticUpperLen(String s, int min, int max)
    {
        if (Validates.isEmpty(s))
            return min == 0;
        
        if (!isAlphabeticUppercase(s, false))
            return false;
        
        return (s.length() >= min && s.length() <= max);
    }

    /** 检查字符串是否都是由字母或数字组成 */
    public static boolean isAlphaNumeric(String s)
    {
        return isAlphaNumeric(s, false);
    }
    
    /** 检查字符串是否都是由字母或数字组成且长度在min,max范围内 */
    public static boolean isAlphaNumericLen(String s, int min, int max)
    {
        if (Validates.isEmpty(s))
            return min == 0;
        
        if (!isAlphaNumeric(s, false))
            return false;
        
        return (s.length() >= min && s.length() <= max);
    }
    
    /** 检查字符串是否都是由字母或数字组成，字母开头 */
    public static boolean isAlphaNumericPA(String s)
    {
        return isAlphaNumericPA(s, false);
    }
    
    /** 检查字符串是否都是由字母或数字组成，字母开头且长度在min,max范围内 */
    public static boolean isAlphaNumericPALen(String s, int min, int max)
    {
        if (Validates.isEmpty(s))
            return min == 0;
        
        if (!isAlphaNumericPA(s, false))
            return false;
        
        return (s.length() >= min && s.length() <= max);
    }
    
    /** 检查字符串是否都是由字母或数字或下划线组成 */
    public static boolean isAlphaNumericUl(String s)
    {
        return isAlphaNumericUl(s, false);
    }
    
    /** 检查字符串是否都是由字母或数字或下划线组成，且长度在min,max范围内 */
    public static boolean isAlphaNumericUlLen(String s, int min, int max)
    {
        if (Validates.isEmpty(s))
            return min == 0;
        
        if (!isAlphaNumericUl(s, false))
            return false;
        
        return (s.length() >= min && s.length() <= max);
    }
    
    /** 检查字符串是否都是由字母或数字或点号组成，且长度在min,max范围内 */
    public static boolean isAlphaNumericDotLen(String s, int min, int max)
    {
        if (Validates.isEmpty(s))
            return min == 0;
        
        if (!isAlphaNumericDot(s, false))
            return false;
        
        return (s.length() >= min && s.length() <= max);
    }
    
    /** 检查字符串是否都是由字母或数字或(.-_~四个URL非特殊字符)组成，且长度在min,max范围内 */
    public static boolean isAlphaNumericUrlLen(String s, int min, int max)
    {
        if (Validates.isEmpty(s))
            return min == 0;
        
        if (!isAlphaNumericUrl(s, false))
            return false;
        
        return (s.length() >= min && s.length() <= max);
    }
    
    /** 检查字符串是否都是由字母或数字或下划线组成，字母或下划线开头 */
    public static boolean isAlphaNumericUlPA(String s)
    {
        return isAlphaNumericUlPA(s, false);
    }
    
    /** 检查字符串是否都是由字母或数字或下划线组成，字母或下划线开头且长度在min,max范围内 */
    public static boolean isAlphaNumericUlPALen(String s, int min, int max)
    {
        if (Validates.isEmpty(s))
            return min == 0;
        
        if (!isAlphaNumericUlPA(s, false))
            return false;
        
        return (s.length() >= min && s.length() <= max);
    }
    
    /** 检查字符串是否都是由字母或数字或下划线或$组成，字母或下划线或$开头 */
    public static boolean isAlphaNumericUlDlPA(String s)
    {
        return isAlphaNumericUlDlPA(s, false);
    }
    
    /** 检查字符串是否都是由字母或数字或下划线或$组成，字母或下划线或$开头且长度在min,max范围内 */
    public static boolean isAlphaNumericUlDlPALen(String s, int min, int max)
    {
        if (Validates.isEmpty(s))
            return min == 0;
        
        if (!isAlphaNumericUlDlPA(s, false))
            return false;
        
        return (s.length() >= min && s.length() <= max);
    }

    /** 检查字符串是否都是由大写字母或数字组成 */
    public static boolean isAlphaUpperNumeric(String s)
    {
        return isAlphaUpperNumeric(s, false);
    }
    
    /** 检查字符串是否都是由大写字母或数字组成 */
    public static boolean isAlphaLowerNumeric(String s)
    {
        return isAlphaLowerNumeric(s, false);
    }

    /** 检查字符串是否都是由大写字母或数字组成 */
    public static boolean isAlphaUpperNumericLen(String s, int min, int max)
    {
        if (Validates.isEmpty(s))
            return min == 0;
        
        if (!isAlphaUpperNumeric(s, false))
            return false;
        
        return (s.length() >= min && s.length() <= max);
    }
    
    /** 检查字符串是否都是由小写字母或数字组成 */
    public static boolean isAlphaLowerNumericLen(String s, int min, int max)
    {
        if (Validates.isEmpty(s))
            return min == 0;
        
        if (!isAlphaLowerNumeric(s, false))
            return false;
        
        return (s.length() >= min && s.length() <= max);
    }
    
    /** 检查字符串是否由十六进制大小写字母和数字组成 */
    public static boolean isHexString(String s)
    {
        return isRegexp(HEX, s, false);
    }
    
    /** 检查字符串是否由十六进制大小写字母和数字组成16位类MD5字符串 */
    public static boolean isMD5String(String s)
    {
        return isRegexp(MD5, s, false);
    }
    
    /** 检查是否是一个浮点型,支持正负号前缀,不支持指点E */
    public static boolean isFloat(String s)
    {
        return isFloat(s, false);
    }
    
    /** 是否非负浮点型，支持多位小数，如12,12.3,12.35,0,0.3,0.35 */
    public static boolean isFloatNonnegative(String s)
    {
        return isFloatNonnegative(s, false);
    }
    
    /** 检查字符串是否是整数、一位小数、或两位小数点的金额值 */
    public static boolean isAmount2R(String s)
    {
        return isAmount2R(s, false);
    }

    /** 检查是否是正确的手机号码 */
    public static boolean isMobile(String s)
    {
        return isMobile(s, false);
    }
    
    /**
     * 判断号码是否符合配置文件所设条件
     * 
     * @param s 号码字符串
     * @return boolean =true 是手机号码,=false 非手机号码
     */
    public static boolean isMobile11(String s)
    {
        return isMobile11(s, false);
    }
    
    /** 检查是否是当天 */
    public static boolean isCurrentDate(String date)
    {
        return DateTimes.getDateString().equals(date);
    }

    /** 检查是否是正确的日期 */
    public static boolean isDate(String date)
    {
        return isDate(date, false);
    }

    /** 检查是否是正确的时间 */
    public static boolean isTime(String time)
    {
        return isTime(time, false);
    }
    
    /** 判断是不是指定的时间格式 */
    public static boolean isDateTime(String datetime)
    {
        return isDateTime(datetime, false);
    }
    
    /** 判断是否是18位身份证号码*/
    public static boolean isIdcard(String idcard)
    {
        return isIdcard(idcard, false);
    }
    
    /** 判断是否是合适的用户名（中文至少2个汉字，字符最小3字符，总长最大25） */
    public static boolean isUserName(String name)
    {
        return isRegexp(USER_CODE, name);
    }
    
    /** 判断是否是合适的密码（大小字母数字和特殊符号四选三，6-16位） */
    public static boolean isUserPass(String password)
    {
        return isRegexp(USER_PASS, password);
    }
    
    /** 检查字符串是否正确的邮件地址(注:要求存在@字符,且不是出现在第一个,最后一个位置,现在不检查是否存在".") */
    public static boolean isEmail(String s)
    {
        return isEmail(s, false);
    }

    /** 检查是否是IP地址,ip为空返回false; */
    public static boolean isIP(String s)
    {
        return isIP(s, false);
    }
    
    /** 检查是否是IP/MASK地址,ip/mask为空返回false; */
    public static boolean isIPMask(String s)
    {
        if (isEmptyBlank(s))
            return false;
        
        s = s.trim();
        int ind = s.indexOf("/");
        if (ind == -1)
        {//没有掩码，可以是一个IP或含*的IP，如192.168.0.3,192.168.000.023或者192.168.*.*
            String[] ipMaskValues = Arrays.toStringArray(s, "\\.");
            if (ipMaskValues.length != 4)
                return false;
            
            for (String ipMaskValue : ipMaskValues)
            {
                if ("*".equals(ipMaskValue))
                    continue;
                
                if (!Validates.isNumericLen(ipMaskValue, 1, 3))
                    return false;//数值型且000-999之间
                
                int value = Integer.parseInt(ipMaskValue);
                if (value > 255)
                    return false;//数字且0-255之间
            }
            
            return true;
        }
        else
        {//有掩码，可以是192.168.0.3/24,或192.168.0.0/255.255.255.192
            String ipMaskIp = s.substring(0, ind).trim();
            String ipMaskMask = s.substring(ind+1).trim();
            if (!isIP(ipMaskIp))
                return false;//前面一段必须是IP
            
            if (isIntegerNonnegative(ipMaskMask))
            {//如果是非负整数，则在0-32之间
                return Integer.parseInt(ipMaskMask) <= 32;
            }
            else
            {//不是数字，则是IP地址掩码，也是0-32，一共33种可能 @see IP_MASK
                if (!isIP(ipMaskMask))
                    return false;
                
                return IPs.ipMaskToInt(ipMaskMask) != -1;
            }
        }
    }
    
    /** 检查IP地址是否允许，支持指定白黑名单 */
    public static boolean isIPAllowByWhiteBlackList(String ip, String ipWhiteList, String ipBlackList, boolean defaultValue)
    {
        if (defaultValue)
        {//默认开放，如果在黑名单中，则返回false
            if (isNotEmptyBlank(ipBlackList))
            {
                List<String> ipList = Lists.toStringList(ipWhiteList);
                if (Validates.isContainIp(ipList, ip))
                    return false;
            }
        }
        else
        {//默认关闭，如果在白名单中，则返回true
            if (isNotEmptyBlank(ipWhiteList))
            {
                List<String> ipList = Lists.toStringList(ipWhiteList);
                if (Validates.isContainIp(ipList, ip))
                    return true;
            }
        }
        
        return defaultValue;
    }
    
    /** 检查是否是macAddress,macAddress为空返回false; */
    public static boolean isMacAddress(String s)
    {
        return isMacAddress(s, false);
    }
    
    /** 检查是否邮编 */
    public static boolean isPostalCode(String s)
    {
        return isPostalCode(s, false);
    }
    
    /** 检查是否整除 */
    public static boolean isDivisible(String val1, String val2) 
    {
        Asserts.as((Validates.isFloat(val1) && Validates.isFloat(val2))?null:"参数都必须是整数或小数");
        
        BigDecimal v1 = new BigDecimal(val1);
        BigDecimal v2 = new BigDecimal(val2);
        
        BigDecimal val = v1.divide(v2, 3, BigDecimal.ROUND_HALF_UP);
        return val.stripTrailingZeros().scale() <= 0;
    }
    
    /************************************************************/
    //日期判断
    /************************************************************/
    
    /** 检查是否是正确的年 */
    public static boolean isYear(String s)
    {
        if (!isNumeric(s))
            return false;
        
        return ((s.length() == 2) || (s.length() == 4));
    }

    /** 判断是否是周末 yyyy-MM-dd */
    public static boolean isWeekend(String date)
    {
        Calendar calendar = DateTimes.toCalendar(date+" 00:00:00");
        return calendar.get(Calendar.DAY_OF_WEEK) == 1;
    }
    
    /** 判断是否季度末 yyyy-MM-dd */
    public static boolean isMonthQuarter(String date)
    {
        if (!isDate(date))
            return false;
        
        String year = date.substring(0, 4);
        String month = date.substring(5, 7);
        String day = date.substring(8);
        
        if (!isMonthLastDay(year, month, day))
            return false;
        
        if (month.equals("03") || month.equals("06") || month.equals("09") || month.equals("12"))
            return true;
        
        return false;
    }
    
    /** 判断是否年末 yyyy-MM-dd */
    public static boolean isYearLastDay(String date)
    {
        if (!isDate(date))
            return false;
        
        String year = date.substring(0, 4);
        String month = date.substring(5, 7);
        String day = date.substring(8);
        
        if (!isMonthLastDay(year, month, day))
            return false;
        
        if (month.equals("12"))
            return true;
        
        return false;
    }
    
    /** 检查是否是正确的月 */
    public static boolean isMonth(String s)
    {
        if (isEmpty(s))
            return false;
        
        return isIntegerValue(s, 1, 12);
    }

    /** 检查是否是正确的日 */
    public static boolean isDay(String s)
    {
        if (isEmpty(s))
            return false;
        
        return isIntegerValue(s, 1, 31);
    }

    /** 检查是否闰年 */
    public static boolean isLeapYear(int year)
    {
        return (year % 4 == 0) && ((!(year % 100 == 0)) || (year % 400 == 0));
    }
    
       /** 检查是否是月末 yyyy-MM-dd */
    public static boolean isMonthLastDay(String date)
    {
        if (!isDate(date))
            return false;
        
        String year = date.substring(0, 4);
        String month = date.substring(5, 7);
        String day = date.substring(8);
        return isMonthLastDay(year, month, day);
    }
    
    /** 检查是否是月末 */
    public static boolean isMonthLastDay(String year, String month, String day)
    {
        if (!isDate(year, month, day))
            return false;
        
        int yearInt = Integer.parseInt(year);
        int monthInt = Integer.parseInt(month);
        int dayInt = Integer.parseInt(day);
        return isMonthLastDay(yearInt, monthInt, dayInt);
    }
    
    /** 检查是否是月末 */
    public static boolean isMonthLastDay(int year, int month, int day)
    {
        if (year < 1000 || year > 9999 || month > 12 || month < 1 || day > 31 || day < 1)
            return false;
        
        switch (month)
        {
        case 1:
        case 3:
        case 5:
        case 7:
        case 8:
        case 10:
        case 12:
            return day == 31;
        case 4:
        case 6:
        case 9:
        case 11:
            return day == 30;
        default://2月
            boolean isLeapYear = Validates.isLeapYear(year);
            return isLeapYear?day==29:day==28;
        }
    }

    /** 检查是否是正确的时 */
    public static boolean isHour(String s)
    {
        if (isEmpty(s))
            return false;
        
        return isIntegerValue(s, 0, 23);
    }

    /** 检查是否是正确的分 */
    public static boolean isMinute(String s)
    {
        if (isEmpty(s))
            return false;
        return isIntegerValue(s, 0, 59);
    }

    /** 检查是否是正确的秒 */
    public static boolean isSecond(String s)
    {
        if (isEmpty(s))
            return false;
        return isIntegerValue(s, 0, 59);
    }

    /** 检查是否是正确的日期 */
    public static boolean isDate(String year, String month, String day)
    {
        if (!(isYear(year) && isMonth(month) && isDay(day)))
            return false;

        int intYear = Integer.parseInt(year);
        int intMonth = Integer.parseInt(month);
        int intDay = Integer.parseInt(day);

        if (intDay > _DAYS_IN_MONTH_[intMonth - 1])
            return false;
        
        if ((intMonth == 2) && (intDay > (isLeapYear(intYear)?29:28)))
            return false;
        
        return true;
    }
    
    /** 判断是不是指定的时间格式, spe为日期分隔符 */
    public static boolean isDateTime(String datetime, String spe)
    {
        if (isEmpty(datetime))
            return false;
        
        datetime = datetime.replaceAll(spe, "-");
        return isDateTime(datetime);
    }
    
    /** 检查是否是西方正确的日期 */
    public static boolean isEnglishDate(String date)
    {
        if (isEmpty(date))
            return false;

        date = date.replaceAll("/", "-");
        return isDate(date);
    }

    /** 检查是否是日期比今天大 */
    public static boolean isDateAfterToday(String date)
    {
        if (!isDate(date))
            return false;
        
        String currentDate = DateTimes.getDateString();
        return date.compareTo(currentDate) > 0;
    }
    
    /** 检查是否是日期比今天大等于 */
    public static boolean isDateAfterEqualToday(String date)
    {
        if (!isDate(date))
            return false;
        
        String currentDate = DateTimes.getDateString();
        return date.compareTo(currentDate) >= 0;
    }
    
    /** 检查是否是正确的时间 */
    public static boolean isTime(String hour, String minute, String second)
    {
        if (isHour(hour) && isMinute(minute) && isSecond(second))
            return true;

        return false;
    }
    
    /**
     * 检查指定时间是否在当前时间规定的范围之内
     * 
     * @param datetime             验证的时间，yyyy-MM-dd HH:mm:ss
     * @param differenceSeconds    范围值，单位秒，
     * @return                     =true表示在时间内，=false表示时间外
     */
    public static boolean isInTime(String datetime, int differenceSeconds)
    {
        long diffTime = System.currentTimeMillis() - DateTimes.toLong(datetime);
        return !((diffTime > 0 && diffTime > differenceSeconds * 1000) || (diffTime < 0 && diffTime < -differenceSeconds * 1000));
    }
    
    /*****************************************************/
    //URL判断，包括HTTP/HTTPS
    /*****************************************************/
    
    /** 判断是否是有效的URL，支持http://和https://大小写开头 */
    public static boolean isUrl(String s)
    {
        return (Strings.startsWithIgnoreCase(s, "http://") || Strings.startsWithIgnoreCase(s, "https://")) && s.length() <= 2083;
    }
    
    /** 判断是否是有效的http URL，支持http://大小写开头 */
    public static boolean isUrlHttp(String s)
    {
        return Strings.startsWithIgnoreCase(s, "http://") && s.length() <= 2083;
    }
    
    /** 判断是否是有效的https URL，支持https://大小写开头 */
    public static boolean isUrlHttps(String s)
    {
        return Strings.startsWithIgnoreCase(s, "https://") && s.length() <= 2083;
    }

    /** 是否是有效的文件名称 */
    public static boolean isFileName(String fileName)
    {
        return isRegexp(FILE_NAME, fileName);
    }
    
    /*****************************************************/
    //数组判断，包括关系
    /*****************************************************/

    /**
     * 字符串按分隔符方式，判断是否包含另一个字符串
     * 
     * @param strs         原字符串
     * @param separator    字符串分隔符
     * @param str          另一字符串
     * @return             =true表示包含,=false表示不包含
     */
    public static boolean isContain(String strs, String separator, String str)
    {
        String[] s = Arrays.toStringArray(strs, separator);
        return isContain(s, str);
    }
    
    /**
     * 字符串按分隔符方式，判断是否包含另一整型
     * 
     * @param strs         原字符串
     * @param separator    字符串分隔符
     * @param i            另一字符串
     * @return             =true表示包含,=false表示不包含
     */
    public static boolean isContain(String strs, String separator, int i)
    {
        int[] s = Arrays.toIntArray(strs, separator);
        return isContain(s, i);
    }
    
    /**
     * 字符串按分隔符方式，判断是否包含另一长整型
     * 
     * @param strs         原字符串
     * @param separator    字符串分隔符
     * @param lo           另一长整型
     * @return             =true表示包含,=false表示不包含
     */
    public static boolean isContain(String strs, String separator, long lo)
    {
        long[] s = Arrays.toLongArray(strs, separator);
        return isContain(s, lo);
    }
    
    /**
     * 字符串数组是否包含另一字符串
     * 
     * @param strs         原字符串
     * @param str          另一字符串
     * @return             =true表示包含,=false表示不包含
     */
    public static boolean isContain(String[] strs, String str)
    {
        if (str == null){
            for (String s : strs){
                if (s == null){
                    return true;
                }
            }
        }else{
            for (String s : strs){
                if (str.equals(s)){
                    return true;
                }
            }
        }
        
        return false;
    }
    
    /**
     * 长整型数组是否包含另一长整型
     * 
     * @param longs     原长整型数组
     * @param lo        另一长整型
     * @return          =true表示包含,=false表示不包含
     */
    public static boolean isContain(long[] longs, long lo)
    {
        for (long l : longs)
        {
            if (l == lo)
                return true;
        }
        
        return false;
    }
    
    /** 检查int是否在int[]内 */
    public static boolean isContain(int[] ints, int io)
    {
        for (int i : ints)
        {
            if (i == io)
                return true;
        }
        
        return false;
    }
    
    /** 检查object是否在列表中 */
    public static boolean isContain(Collection<?> collection, Object o)
    {
        return Lists.contains(collection, o);
    }
    
    /** 检查IP是否有包括IP掩码的IP集中 */
    public static boolean isContainIp(List<String> ipMaskList, String ip)
    {
        if (!isIP(ip))
            return false;
        
        for (String ipMask : ipMaskList)
        {
            if (isContainIp(ipMask, ip))
                return true;
        }
        
        return false;
    }
    
    /** 检查IP是否有包括IP掩码的IP集中，注意如果ipMask和ip无效返回false */
    public static boolean isContainIp(String ipMask, String ip)
    {
        if (!isIP(ip) || !isIPMask(ipMask))
            return false;
        
        //本身就是IP
        if (ipMask.equals(ip))
            return true;
        
        ip = IPs.trim(ip);
        int[] ipValues = Arrays.toIntArray(ip, "\\.");
        
        int ind = ipMask.indexOf("/");
        if (ind == -1)
        {//没有掩码，格式为192.168.*.*
            String[] ipMaskValues = Arrays.toStringArray(ipMask, "\\.");
            for (int i=0;i<ipMaskValues.length;i++)
            {
                if ("*".equals(ipMaskValues[i]))
                    continue;
                
                if (Integer.parseInt(ipMaskValues[i]) != ipValues[i])
                    return false;
            }
            return true;
        }
        else
        {//有掩码，格式为192.168.0.0/24，或192.168.0.0/255.255.255.192这两种，都转为int格式0-32
            String ipMaskIp = ipMask.substring(0, ind);
            String ipMaskMask = ipMask.substring(ind+1);
            
            //求得掩码值
            int ipMaskI = -1;
            if (isIntegerNonnegative(ipMaskMask))
                ipMaskI = Integer.parseInt(ipMaskMask);
            else
                ipMaskI = IPs.ipMaskToInt(ipMaskMask);
            
            //计算允许的最小最大IP值
            long[] minMaxIp = IPs.calcIpMaskMinMax(ipMaskIp, ipMaskI);
            long ipLong = IPs.ipToLong(ip);
            return ipLong >= minMaxIp[0] && ipLong <= minMaxIp[1];
        }
    }
    
    /** 检查src是否包含字符串数组任何一个 */
    public static boolean isStrContainStrArr(String src, String[] strs)
    {
        if (isEmpty(src) || strs == null || strs.length == 0)
            return false;
        
        for (String str : strs)
        {
            if (src.contains(str))
                return true;
        }
        
        return false;
    }
    
    /** 检查src是否包含字符串数组任何一个 */
    public static boolean isStrContainStrArr(String src, String strs, String split)
    {
        return isStrContainStrArr(src, strs.split(split));
    }
    
    /**
     * 源src是否是含有通配符*的pattern能匹配上，如/abc/cde.gif匹配上/a*c/*de.*
     *
     * @param s         字符串
     * @param pattern   含通配符*的字符串，注意该方法中*至少占一个字符
     * @return
     */
    public static boolean isMatch(String s, String pattern)
    {
        if (s == null || pattern == null)
            return false;
        
        int ind = pattern.indexOf("*");
        if (ind == -1)
        {//如果匹配字符串不含通配符，则比较两字符串是否相等
            return s.equals(pattern);
        }
        
        while (true)
        {
            if (s.length() <= ind)
            {//长度不够没匹配上
                return false;
            }
            
            if (ind > 0)
            {//取出*号前面的内容
                String match = pattern.substring(0, ind);
                if (!s.startsWith(match))
                {//没有匹配上*号前面的内容
                    return false;
                }
                
                //抹除*号前面的内容，使得*号在第一个位置上
                pattern = pattern.substring(ind);
                s = s.substring(ind);
            }
            
            //去除*号
            pattern = pattern.substring(1);
            if (isEmpty(pattern))
            {//*号是最后一个字符表示成功
                return true;
            }
            
            //由于*号必须占一个字符，所以s也需要先去除一个字符再比较
            s = s.substring(1);
            
            //下一个*号
            ind = pattern.indexOf("*");
            if (ind == -1)
            {//后面没有*号，比较是否后缀匹配成功
                if (!s.endsWith(pattern))
                {//没匹配成功
                    return false;
                }
                
                return true;
            }
            
            String match = pattern.substring(0, ind);
            int aInd = s.indexOf(match);
            if (aInd == -1)
            {//没有匹配下一段内容
                return false;
            }
            
            if (aInd > 0)
            {//查到*号对应的内容之前的去除，保证以match开头，以便进入下一个*号循环时能startsWith==true
                s = s.substring(aInd);
            }
        }
    }
    
    /**
     * 源src是否是含有通配符*的pattern能匹配上，匹配上一个即成功，如/abc/cde.gif匹配上/a*c/*de.*
     *
     * @param s         字符串
     * @param patterns  含通配符*的字符串数组，注意该方法中*至少占一个字符
     * @return
     */
    public static boolean isMatch(String s, String... patterns)
    {
        for (String pattern : patterns)
        {
            if (isMatch(s, pattern))
                return true;
        }
        return false;
    }
}
